"""
告警管理器

提供告警生成、管理、通知等功能。
"""

from enum import Enum
from typing import Dict, List, Optional, Any, Callable, Union
from datetime import datetime, timedelta
from dataclasses import dataclass, field
import asyncio
import json
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import requests
from decimal import Decimal

from .monitor import MonitorEvent, MonitorLevel


class AlertLevel(Enum):
    """告警级别枚举"""
    LOW = "low"  # 低级别
    MEDIUM = "medium"  # 中级别
    HIGH = "high"  # 高级别
    CRITICAL = "critical"  # 严重级别


class AlertType(Enum):
    """告警类型枚举"""
    THRESHOLD = "threshold"  # 阈值告警
    TREND = "trend"  # 趋势告警
    ANOMALY = "anomaly"  # 异常告警
    SYSTEM = "system"  # 系统告警
    TRADING = "trading"  # 交易告警
    RISK = "risk"  # 风险告警
    PERFORMANCE = "performance"  # 性能告警
    CUSTOM = "custom"  # 自定义告警


class AlertStatus(Enum):
    """告警状态枚举"""
    ACTIVE = "active"  # 活跃
    ACKNOWLEDGED = "acknowledged"  # 已确认
    RESOLVED = "resolved"  # 已解决
    SUPPRESSED = "suppressed"  # 已抑制
    EXPIRED = "expired"  # 已过期


class NotificationChannel(Enum):
    """通知渠道枚举"""
    EMAIL = "email"  # 邮件
    SMS = "sms"  # 短信
    WEBHOOK = "webhook"  # Webhook
    DINGTALK = "dingtalk"  # 钉钉
    WECHAT = "wechat"  # 微信
    SLACK = "slack"  # Slack
    CONSOLE = "console"  # 控制台


@dataclass
class AlertRule:
    """告警规则"""
    rule_id: str  # 规则ID
    name: str  # 规则名称
    description: str = ""  # 规则描述
    
    # 触发条件
    condition: str = ""  # 条件表达式
    threshold: Optional[float] = None  # 阈值
    operator: str = ">"  # 操作符 (>, <, >=, <=, ==, !=)
    
    # 告警配置
    alert_type: AlertType = AlertType.THRESHOLD
    alert_level: AlertLevel = AlertLevel.MEDIUM
    
    # 时间配置
    evaluation_interval: float = 60.0  # 评估间隔（秒）
    duration: float = 300.0  # 持续时间（秒）
    cooldown: float = 3600.0  # 冷却时间（秒）
    
    # 通知配置
    notification_channels: List[NotificationChannel] = field(default_factory=list)
    notification_template: Optional[str] = None  # 通知模板
    
    # 启用状态
    enabled: bool = True
    
    # 元数据
    tags: Dict[str, str] = field(default_factory=dict)
    metadata: Dict[str, Any] = field(default_factory=dict)


@dataclass
class Alert:
    """告警"""
    alert_id: str  # 告警ID
    rule_id: str  # 规则ID
    title: str  # 告警标题
    message: str  # 告警消息
    
    # 告警属性
    alert_type: AlertType  # 告警类型
    alert_level: AlertLevel  # 告警级别
    status: AlertStatus = AlertStatus.ACTIVE  # 告警状态
    
    # 时间信息
    created_at: datetime = field(default_factory=datetime.now)  # 创建时间
    updated_at: datetime = field(default_factory=datetime.now)  # 更新时间
    acknowledged_at: Optional[datetime] = None  # 确认时间
    resolved_at: Optional[datetime] = None  # 解决时间
    
    # 触发信息
    trigger_value: Optional[float] = None  # 触发值
    threshold_value: Optional[float] = None  # 阈值
    
    # 关联信息
    source: Optional[str] = None  # 告警源
    monitor_id: Optional[str] = None  # 监控器ID
    
    # 处理信息
    acknowledged_by: Optional[str] = None  # 确认人
    resolved_by: Optional[str] = None  # 解决人
    resolution_note: Optional[str] = None  # 解决备注
    
    # 通知信息
    notification_sent: bool = False  # 是否已发送通知
    notification_count: int = 0  # 通知次数
    last_notification_at: Optional[datetime] = None  # 最后通知时间
    
    # 元数据
    tags: Dict[str, str] = field(default_factory=dict)
    metadata: Dict[str, Any] = field(default_factory=dict)


@dataclass
class NotificationConfig:
    """通知配置"""
    channel: NotificationChannel  # 通知渠道
    enabled: bool = True  # 是否启用
    
    # 邮件配置
    smtp_server: Optional[str] = None
    smtp_port: int = 587
    smtp_username: Optional[str] = None
    smtp_password: Optional[str] = None
    email_from: Optional[str] = None
    email_to: List[str] = field(default_factory=list)
    
    # Webhook配置
    webhook_url: Optional[str] = None
    webhook_headers: Dict[str, str] = field(default_factory=dict)
    
    # 钉钉配置
    dingtalk_token: Optional[str] = None
    dingtalk_secret: Optional[str] = None
    
    # 其他配置
    config: Dict[str, Any] = field(default_factory=dict)


class AlertEvaluator:
    """告警评估器"""
    
    def __init__(self):
        """初始化告警评估器"""
        self._operators = {
            ">": lambda x, y: x > y,
            "<": lambda x, y: x < y,
            ">=": lambda x, y: x >= y,
            "<=": lambda x, y: x <= y,
            "==": lambda x, y: x == y,
            "!=": lambda x, y: x != y
        }
    
    def evaluate_rule(
        self,
        rule: AlertRule,
        metrics: Dict[str, Any],
        context: Optional[Dict[str, Any]] = None
    ) -> bool:
        """
        评估告警规则
        
        Args:
            rule: 告警规则
            metrics: 指标数据
            context: 上下文数据
            
        Returns:
            bool: 是否触发告警
        """
        try:
            # 简单阈值检查
            if rule.threshold is not None and rule.condition in metrics:
                value = metrics[rule.condition]
                if isinstance(value, (int, float, Decimal)):
                    operator_func = self._operators.get(rule.operator)
                    if operator_func:
                        return operator_func(float(value), rule.threshold)
            
            # 复杂条件表达式（简化版）
            if rule.condition:
                # 这里可以实现更复杂的条件评估逻辑
                # 比如使用 eval() 或专门的表达式解析器
                pass
            
            return False
            
        except Exception as e:
            print(f"告警规则评估失败: {e}")
            return False
    
    def create_alert_from_rule(
        self,
        rule: AlertRule,
        trigger_value: Optional[float] = None,
        context: Optional[Dict[str, Any]] = None
    ) -> Alert:
        """从规则创建告警"""
        alert_id = f"{rule.rule_id}_{int(datetime.now().timestamp())}"
        
        return Alert(
            alert_id=alert_id,
            rule_id=rule.rule_id,
            title=f"告警: {rule.name}",
            message=rule.description or f"规则 {rule.name} 被触发",
            alert_type=rule.alert_type,
            alert_level=rule.alert_level,
            trigger_value=trigger_value,
            threshold_value=rule.threshold,
            tags=rule.tags.copy(),
            metadata=context or {}
        )


class NotificationSender:
    """通知发送器"""
    
    def __init__(self, config: NotificationConfig):
        """
        初始化通知发送器
        
        Args:
            config: 通知配置
        """
        self.config = config
    
    async def send_notification(self, alert: Alert) -> bool:
        """
        发送通知
        
        Args:
            alert: 告警信息
            
        Returns:
            bool: 是否发送成功
        """
        try:
            if self.config.channel == NotificationChannel.EMAIL:
                return await self._send_email(alert)
            elif self.config.channel == NotificationChannel.WEBHOOK:
                return await self._send_webhook(alert)
            elif self.config.channel == NotificationChannel.DINGTALK:
                return await self._send_dingtalk(alert)
            elif self.config.channel == NotificationChannel.CONSOLE:
                return await self._send_console(alert)
            else:
                print(f"不支持的通知渠道: {self.config.channel}")
                return False
                
        except Exception as e:
            print(f"发送通知失败: {e}")
            return False
    
    async def _send_email(self, alert: Alert) -> bool:
        """发送邮件通知"""
        if not all([
            self.config.smtp_server,
            self.config.smtp_username,
            self.config.smtp_password,
            self.config.email_from,
            self.config.email_to
        ]):
            return False
        
        try:
            # 创建邮件
            msg = MIMEMultipart()
            msg['From'] = self.config.email_from or ""
            msg['To'] = ', '.join(self.config.email_to)
            msg['Subject'] = f"[{alert.alert_level.value.upper()}] {alert.title}"
            
            # 邮件内容
            body = f"""
告警详情:
- 告警ID: {alert.alert_id}
- 告警级别: {alert.alert_level.value}
- 告警类型: {alert.alert_type.value}
- 创建时间: {alert.created_at}
- 消息: {alert.message}
"""
            if alert.trigger_value is not None:
                body += f"- 触发值: {alert.trigger_value}\n"
            if alert.threshold_value is not None:
                body += f"- 阈值: {alert.threshold_value}\n"
            
            msg.attach(MIMEText(body, 'plain', 'utf-8'))
            
            # 发送邮件
            server = smtplib.SMTP(self.config.smtp_server or "", self.config.smtp_port)
            server.starttls()
            server.login(self.config.smtp_username or "", self.config.smtp_password or "")
            server.send_message(msg)
            server.quit()
            
            return True
            
        except Exception as e:
            print(f"发送邮件失败: {e}")
            return False
    
    async def _send_webhook(self, alert: Alert) -> bool:
        """发送Webhook通知"""
        if not self.config.webhook_url:
            return False
        
        try:
            payload = {
                "alert_id": alert.alert_id,
                "title": alert.title,
                "message": alert.message,
                "level": alert.alert_level.value,
                "type": alert.alert_type.value,
                "status": alert.status.value,
                "created_at": alert.created_at.isoformat(),
                "trigger_value": alert.trigger_value,
                "threshold_value": alert.threshold_value,
                "tags": alert.tags,
                "metadata": alert.metadata
            }
            
            headers = {"Content-Type": "application/json"}
            headers.update(self.config.webhook_headers)
            
            response = requests.post(
                self.config.webhook_url,
                json=payload,
                headers=headers,
                timeout=10
            )
            
            return response.status_code == 200
            
        except Exception as e:
            print(f"发送Webhook失败: {e}")
            return False
    
    async def _send_dingtalk(self, alert: Alert) -> bool:
        """发送钉钉通知"""
        if not self.config.dingtalk_token:
            return False
        
        try:
            url = f"https://oapi.dingtalk.com/robot/send?access_token={self.config.dingtalk_token}"
            
            # 构建消息
            level_emoji = {
                AlertLevel.LOW: "🟢",
                AlertLevel.MEDIUM: "🟡", 
                AlertLevel.HIGH: "🟠",
                AlertLevel.CRITICAL: "🔴"
            }
            
            emoji = level_emoji.get(alert.alert_level, "⚠️")
            
            text = f"{emoji} **{alert.title}**\n\n"
            text += f"**级别**: {alert.alert_level.value}\n"
            text += f"**类型**: {alert.alert_type.value}\n"
            text += f"**时间**: {alert.created_at.strftime('%Y-%m-%d %H:%M:%S')}\n"
            text += f"**消息**: {alert.message}\n"
            
            if alert.trigger_value is not None:
                text += f"**触发值**: {alert.trigger_value}\n"
            if alert.threshold_value is not None:
                text += f"**阈值**: {alert.threshold_value}\n"
            
            payload = {
                "msgtype": "markdown",
                "markdown": {
                    "title": alert.title,
                    "text": text
                }
            }
            
            response = requests.post(url, json=payload, timeout=10)
            return response.status_code == 200
            
        except Exception as e:
            print(f"发送钉钉通知失败: {e}")
            return False
    
    async def _send_console(self, alert: Alert) -> bool:
        """发送控制台通知"""
        level_colors = {
            AlertLevel.LOW: "\033[92m",  # 绿色
            AlertLevel.MEDIUM: "\033[93m",  # 黄色
            AlertLevel.HIGH: "\033[91m",  # 红色
            AlertLevel.CRITICAL: "\033[95m"  # 紫色
        }
        
        color = level_colors.get(alert.alert_level, "\033[0m")
        reset = "\033[0m"
        
        print(f"{color}[ALERT] {alert.title}{reset}")
        print(f"  级别: {alert.alert_level.value}")
        print(f"  类型: {alert.alert_type.value}")
        print(f"  时间: {alert.created_at}")
        print(f"  消息: {alert.message}")
        
        if alert.trigger_value is not None:
            print(f"  触发值: {alert.trigger_value}")
        if alert.threshold_value is not None:
            print(f"  阈值: {alert.threshold_value}")
        
        return True


class AlertManager:
    """
    告警管理器
    
    管理告警规则、评估告警条件、发送通知等。
    """
    
    def __init__(self):
        """初始化告警管理器"""
        self._rules: Dict[str, AlertRule] = {}
        self._alerts: Dict[str, Alert] = {}
        self._notification_configs: Dict[NotificationChannel, NotificationConfig] = {}
        self._evaluator = AlertEvaluator()
        
        # 运行状态
        self._running = False
        self._evaluation_task: Optional[asyncio.Task] = None
        
        # 统计信息
        self.total_alerts = 0
        self.active_alerts = 0
        self.resolved_alerts = 0
        
        # 回调函数
        self._alert_callbacks: List[Callable[[Alert], None]] = []
    
    def add_rule(self, rule: AlertRule) -> None:
        """
        添加告警规则
        
        Args:
            rule: 告警规则
        """
        self._rules[rule.rule_id] = rule
    
    def remove_rule(self, rule_id: str) -> None:
        """
        移除告警规则
        
        Args:
            rule_id: 规则ID
        """
        if rule_id in self._rules:
            del self._rules[rule_id]
    
    def get_rule(self, rule_id: str) -> Optional[AlertRule]:
        """获取告警规则"""
        return self._rules.get(rule_id)
    
    def get_all_rules(self) -> List[AlertRule]:
        """获取所有告警规则"""
        return list(self._rules.values())
    
    def add_notification_config(self, config: NotificationConfig) -> None:
        """
        添加通知配置
        
        Args:
            config: 通知配置
        """
        self._notification_configs[config.channel] = config
    
    def evaluate_metrics(
        self,
        metrics: Dict[str, Any],
        context: Optional[Dict[str, Any]] = None
    ) -> List[Alert]:
        """
        评估指标并生成告警
        
        Args:
            metrics: 指标数据
            context: 上下文数据
            
        Returns:
            List[Alert]: 生成的告警列表
        """
        new_alerts = []
        
        for rule in self._rules.values():
            if not rule.enabled:
                continue
            
            # 检查冷却时间
            if self._is_in_cooldown(rule):
                continue
            
            # 评估规则
            if self._evaluator.evaluate_rule(rule, metrics, context):
                # 创建告警
                trigger_value = metrics.get(rule.condition) if rule.condition else None
                alert = self._evaluator.create_alert_from_rule(
                    rule, 
                    trigger_value, 
                    context
                )
                
                # 添加告警
                self._add_alert(alert)
                new_alerts.append(alert)
                
                # 发送通知
                asyncio.create_task(self._send_alert_notifications(alert))
        
        return new_alerts
    
    def _is_in_cooldown(self, rule: AlertRule) -> bool:
        """检查规则是否在冷却期"""
        # 查找该规则的最近告警
        recent_alerts = [
            alert for alert in self._alerts.values()
            if alert.rule_id == rule.rule_id and 
               alert.status == AlertStatus.ACTIVE and
               (datetime.now() - alert.created_at).total_seconds() < rule.cooldown
        ]
        
        return len(recent_alerts) > 0
    
    def _add_alert(self, alert: Alert) -> None:
        """添加告警"""
        self._alerts[alert.alert_id] = alert
        self.total_alerts += 1
        self.active_alerts += 1
        
        # 调用回调函数
        for callback in self._alert_callbacks:
            try:
                callback(alert)
            except Exception as e:
                print(f"告警回调执行失败: {e}")
    
    async def _send_alert_notifications(self, alert: Alert) -> None:
        """发送告警通知"""
        rule = self._rules.get(alert.rule_id)
        if not rule:
            return
        
        success_count = 0
        
        for channel in rule.notification_channels:
            config = self._notification_configs.get(channel)
            if not config or not config.enabled:
                continue
            
            sender = NotificationSender(config)
            success = await sender.send_notification(alert)
            
            if success:
                success_count += 1
        
        # 更新通知状态
        if success_count > 0:
            alert.notification_sent = True
            alert.notification_count += 1
            alert.last_notification_at = datetime.now()
    
    def acknowledge_alert(self, alert_id: str, acknowledged_by: str = "system") -> bool:
        """
        确认告警
        
        Args:
            alert_id: 告警ID
            acknowledged_by: 确认人
            
        Returns:
            bool: 是否成功
        """
        alert = self._alerts.get(alert_id)
        if not alert or alert.status != AlertStatus.ACTIVE:
            return False
        
        alert.status = AlertStatus.ACKNOWLEDGED
        alert.acknowledged_at = datetime.now()
        alert.acknowledged_by = acknowledged_by
        alert.updated_at = datetime.now()
        
        return True
    
    def resolve_alert(
        self,
        alert_id: str,
        resolved_by: str = "system",
        resolution_note: Optional[str] = None
    ) -> bool:
        """
        解决告警
        
        Args:
            alert_id: 告警ID
            resolved_by: 解决人
            resolution_note: 解决备注
            
        Returns:
            bool: 是否成功
        """
        alert = self._alerts.get(alert_id)
        if not alert or alert.status == AlertStatus.RESOLVED:
            return False
        
        alert.status = AlertStatus.RESOLVED
        alert.resolved_at = datetime.now()
        alert.resolved_by = resolved_by
        alert.resolution_note = resolution_note
        alert.updated_at = datetime.now()
        
        if alert.status == AlertStatus.ACTIVE:
            self.active_alerts -= 1
        self.resolved_alerts += 1
        
        return True
    
    def get_alert(self, alert_id: str) -> Optional[Alert]:
        """获取告警"""
        return self._alerts.get(alert_id)
    
    def get_active_alerts(self) -> List[Alert]:
        """获取活跃告警"""
        return [
            alert for alert in self._alerts.values()
            if alert.status == AlertStatus.ACTIVE
        ]
    
    def get_alerts_by_level(self, level: AlertLevel) -> List[Alert]:
        """按级别获取告警"""
        return [
            alert for alert in self._alerts.values()
            if alert.alert_level == level
        ]
    
    def get_alerts_summary(self) -> Dict[str, Any]:
        """获取告警摘要"""
        return {
            "total_alerts": self.total_alerts,
            "active_alerts": self.active_alerts,
            "resolved_alerts": self.resolved_alerts,
            "alerts_by_level": {
                level.value: len(self.get_alerts_by_level(level))
                for level in AlertLevel
            },
            "alerts_by_type": {
                alert_type.value: len([
                    alert for alert in self._alerts.values()
                    if alert.alert_type == alert_type
                ])
                for alert_type in AlertType
            }
        }
    
    def add_alert_callback(self, callback: Callable[[Alert], None]) -> None:
        """添加告警回调"""
        self._alert_callbacks.append(callback)
    
    def remove_alert_callback(self, callback: Callable[[Alert], None]) -> None:
        """移除告警回调"""
        if callback in self._alert_callbacks:
            self._alert_callbacks.remove(callback)
    
    def clear_resolved_alerts(self, older_than: Optional[timedelta] = None) -> int:
        """
        清理已解决的告警
        
        Args:
            older_than: 清理多久之前的告警
            
        Returns:
            int: 清理的告警数量
        """
        if older_than is None:
            older_than = timedelta(days=7)  # 默认清理7天前的告警
        
        cutoff_time = datetime.now() - older_than
        
        alerts_to_remove = [
            alert_id for alert_id, alert in self._alerts.items()
            if alert.status == AlertStatus.RESOLVED and
               alert.resolved_at and
               alert.resolved_at < cutoff_time
        ]
        
        for alert_id in alerts_to_remove:
            del self._alerts[alert_id]
        
        return len(alerts_to_remove)